"""
装饰器

    装饰器允许在不修改原有函数代码的基础上，动态地增加或修改函数的功能。装饰器本质上是一个接收函数作为输入并返回一个新的包装过后的函数的对象。

"""

# 1.1 定义一个简单的装饰器函数
def decorator(func):
    def wrapper(*args, **kwargs):
        print("=====> Before calling the function.")
        result = func(*args, **kwargs)
        print("<===== After calling the function.")
        return result
    return wrapper

# 1.2 使用装饰器装饰函数
# 当我们使用 @decorator 前缀在 func 定义前，Python会自动将 add 作为参数传递给 decorator，然后将返回的 wrapper 函数替换掉原来的 add
# @后面的 decorator 不是固定的，起觉于我们定义的装饰器函数的名字。 
@decorator
def add(num1: int, num2: int) -> int:
    return num1 + num2

# 1.3 调用被装饰的函数
print(add(3, 5))

"""
多层装饰器
"""

# 2.1 定义多个装饰器函数
def decorator1(func):
    def wrapper(*args, **kwargs):
        print("=====> Entering decorator1.")
        result = func(*args, **kwargs)
        print("<===== Exiting decorator1.")
        return result
    return wrapper

def decorator2(func):
    def wrapper(*args, **kwargs):
        print("=====> Entering decorator2.")
        result = func(*args, **kwargs)
        print("<===== Exiting decorator2.")
        return result
    return wrapper

# 2.2 使用多个装饰器装饰函数
@decorator1
@decorator2
def multiply(num1: int, num2: int) -> int:
    return num1 * num2

# 2.3 调用被多层装饰的函数
print(multiply(4, 6))

"""
类装饰器
    类装饰器是包含 __call__() 方法的类，它接受函数作为参数，并返回新的函数。
"""
# 3.1 定义一个类装饰器
class ClassDecorator:   
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("=====> Before calling the function in ClassDecorator.")
        result = self.func(*args, **kwargs)
        print("<===== After calling the function in ClassDecorator.")
        return result
# 3.2 使用类装饰器装饰函数
@ClassDecorator
def subtract(num1: int, num2: int) -> int:
    return num1 - num2  
# 3.3 调用被类装饰的函数
print(subtract(10, 4))

"""
    带参数的装饰器
"""
# 4.1 定义一个带参数的装饰器
def parameterized_decorator(prefix: str):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"{prefix}=====> Before calling the function.")
            result = func(*args, **kwargs)
            print(f"{prefix}<===== After calling the function.")
            return result
        return wrapper
    return decorator
# 4.2 使用带参数的装饰器装饰函数
@parameterized_decorator("LOG: ")
def divide(num1: int, num2: int) -> float:
    return num1 / num2
# 4.3 调用被带参数装饰的函数
print(divide(20, 4))

